package de.upb.aquarium.water;

import java.util.ArrayList;
import java.util.List;

public abstract class WaveBuffer {
	public enum InterpolationMode
	{
		LINEAR,
		NEAREST
	}
	
	private int horizontalResolution, verticalResolution;
	private double width, depth;
		
	private List<Wave> waves;

	private double lifeTime;
	
	private double[][] buffer;
	private double[] currentBuffer;
	
	public abstract double getMaximalLifeTime();
	protected abstract void createWaves();
	
	public WaveBuffer(
			int horizontalResolution,
			int verticalResolution,
			double width,
			double depth,
			int timeResolution) {
		super();
	
		this.horizontalResolution = horizontalResolution;
		this.verticalResolution = verticalResolution;
		this.width = width;
		this.depth = depth;

		buffer = new double[timeResolution+1][horizontalResolution*verticalResolution];
		waves = new ArrayList<Wave>();
	}
		
	protected void init()
	{
		createWaves();
		this.lifeTime = getMaximalLifeTime();
		
		for (int t = 0; t < buffer.length; t++)
		{
			currentBuffer = buffer[t];
			double time = (double)t/(double)(buffer.length-1)*lifeTime;
			
			for (Wave w : waves)
			{
				w.setTime(time);
			}
			
			for (int i = 0; i < horizontalResolution; i++)
			{
				double x = (i*width)/(horizontalResolution-1);
				for (int j = 0; j < verticalResolution; j++)
				{
					double y = (j*depth)/(verticalResolution-1);
					currentBuffer[j*horizontalResolution+i] = calcHigh(x,y);
				}
			}
		}
		
		currentBuffer = null;
	}
	
	public void setCurrentTime(double time, InterpolationMode mode)
	{
		double t = (time/lifeTime)*(buffer.length-1);
		
		if (mode == InterpolationMode.NEAREST)
		{
			currentBuffer = buffer[(int)Math.round(t)];
		}
		else
		{
			currentBuffer = new double[horizontalResolution*verticalResolution];
			
			int floor = (int)Math.floor(t);
			int ceil = (int)Math.ceil(t);
			double floorFactor = t - floor;
			double ceilFactor = ceil - t;
			
			for (int i = 0; i < currentBuffer.length; i++)
			{
				currentBuffer[i] = floorFactor*buffer[floor][i] + ceilFactor*buffer[ceil][i];
			}
		}
	}
	
	public double getValueAt(int i, int j)
	{
		return currentBuffer[j*horizontalResolution + i];
	}
	
	private double calcHigh(double x, double y) {
		
		double result = 0.0;
		
		for (Wave w : waves)
		{
			result += w.getHighAt(x, y);
		}
		
		return result;
	}

	public void addWave(Wave wave)
	{
		waves.add(wave);
	}
	
	public int getHorizontalResolution() {
		return horizontalResolution;
	}

	public void setHorizontalResolution(int horizontalResolution) {
		this.horizontalResolution = horizontalResolution;
	}

	public int getVerticalResolution() {
		return verticalResolution;
	}

	public void setVerticalResolution(int verticalResolution) {
		this.verticalResolution = verticalResolution;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public double getDepth() {
		return depth;
	}

	public void setDepth(double depth) {
		this.depth = depth;
	}
}
